package com.byit.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.byit.config.MinioConfig;
import com.byit.dto.ProCreateOrderDto;
import com.byit.msg.MessageResult;
import com.byit.util.*;
import com.byit.utils.*;
import org.apache.http.protocol.HTTP;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URLDecoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException;
import java.util.HashMap;
import java.util.Map;

/**
 ted by Administrator on 2019/1/25.
 */

@Service
public class BestPayService {

    private static Logger logger = Logger.getLogger(BestPayService.class);

    @Autowired
    private MinioConfig minioConfig;

    private String URL="https://capi.bestpay.com.cn/common/interface";

    /* 以下4个参数通过获取公钥接口返回获得 */
    // RSA公钥
    private String publicKey;
    // RSA公钥index
    private String indexKey;
    // 会话超时控制
    private String sessionId;
    // 加密因子索引
    private String aesIndex;

    /**
     *
     * 下单接口
     */
    public String proCreateOrder(ProCreateOrderDto proCreateOrderDto) throws IOException, InvalidKeySpecException, NoSuchAlgorithmException, InvalidKeyException, SignatureException {


        String url = "https://mapi.bestpay.com.cn/mapi/uniformReceipt/proCreateOrder"; //生产环境

        JSONObject data = new JSONObject();
        data.put("service_identify","");   //请根据商户业务填写具体的参数值
        data.put("subject","");//请根据商户业务填写具体的参数值
        data.put("product_type",""); //请根据商户业务填写具体的参数值
        data.put("boby", "");
        data.put("goods_count",1);
        data.put("service_cardno",""); //请根据商户业务填写具体的参数值
        String riskControlInfo ="["+data+"]";//汇率
        //请求参数
        Map<String,String> translateResultData=new HashMap<String,String>();
        translateResultData.put("institutionCode", Params.INSTITUTIONCODE);
        translateResultData.put("institutionType",Params.INSTITUTIONTYPE);
        translateResultData.put("merchantNo",Params.merchantNo);
        translateResultData.put("outTradeNo",proCreateOrderDto.getOutTradeNo());
        translateResultData.put("tradeAmt", YuanRpmUtil.getTradeAmt(proCreateOrderDto.getTradeAmt()));   // 分
        translateResultData.put("subject",proCreateOrderDto.getSubject());
        translateResultData.put("requestDate",Params.requestDate);
        //  translateResultData.put("timeout",bean.timeout);
        translateResultData.put("goodsInfo",proCreateOrderDto.getGoodsInfo());
        //TODO 回调url
        translateResultData.put("notifyUrl",minioConfig.getPayCallBackUrl()+"/app/pay/callbackPay");
        translateResultData.put("operator",Params.operator);
        translateResultData.put("tradeChannel",Params.tradeChannel);
        translateResultData.put("accessCode",Params.accessCode);
        translateResultData.put("mediumType",Params.mediumType);
        translateResultData.put("ccy",Params.ccy);
        translateResultData.put("riskControlInfo",riskControlInfo);

        //对translateResultData按照字典顺序排序，获得content
        String content = HttpClientPost.assembelSignaturingData(translateResultData);
        //对content1进行加密获得sign
        String sign = SignatureUtil.sign(InspectionSign.getSignture(),content, (PrivateKey) InspectionSign.getKey().getPrivateKey());
        translateResultData.put("sign",sign);
        String s = JSON.toJSONString(translateResultData);
        String resposeData = HttpClientPost.reqPost(url, s);
        return  resposeData;

    }


    /**
     * 下单并唤起收银台url
     * @return
     * @throws Exception
     */
    public String getUrl(ProCreateOrderDto proCreateOrderDto ) throws Exception {
        String result = proCreateOrder(proCreateOrderDto);
        //获取收银台url
        return webUrl(result,proCreateOrderDto.getTradeAmt(),proCreateOrderDto.getSubject(),proCreateOrderDto.getGoodsInfo());
    }



    /**
     * 获取公钥
     *
     * @return 公钥json
     * @throws IOException
     */
    private String getPublicKey() throws IOException {
        JSONObject jsonParams = new JSONObject();
        jsonParams.put("keyIndex", "");
        jsonParams.put("encryKey", "");
        jsonParams.put("encryStr", "");
        jsonParams.put("interCode", "INTER.SYSTEM.001");
        jsonParams.put("version", "v1.3");
        String requestBody = jsonParams.toJSONString();
        String responseStr = HttpRequestUtils.doPost(URL, requestBody);
        JSONObject respObj = JSONObject.parseObject(responseStr);
        String isSuccess = respObj.getString("success");
        String resultStr = respObj.getString("result");
        if (Boolean.parseBoolean(isSuccess)) {
            JSONObject resultObj = JSONObject.parseObject(resultStr);
            String _keyIndex = resultObj.getString("keyIndex");
            String _pubKey = resultObj.getString("pubKey");
            String _sessionId = resultObj.getString("sessionId");
            String _aesIndex = resultObj.getString("aesIndex");
            if (_keyIndex.length() + _pubKey.length() + _sessionId.length() > 3) {
                this.indexKey = _keyIndex;
                this.publicKey = _pubKey;
                this.sessionId = _sessionId;
                this.aesIndex = _aesIndex;
            }
        }
        logger.info("公钥接口请求："+requestBody);
        logger.info("公钥接口返回："+responseStr);
        return responseStr;
    }

    /**
     * 获取支付url
     * @param getresult
     * @return
     * @throws Exception
     */
    public String webUrl(String getresult,String tradeAmt,String subject,String goodsInfo) throws Exception {
        // 获取公钥
        String publicKeyJson = this.getPublicKey();
        Object result = JSONObject.parseObject(publicKeyJson, Resp.class).getResult();
        RsaKeyBO rsaKeyBO = JSONObject.parseObject(result.toString(), RsaKeyBO.class);
        //获取唤起收银台需要的参数
        Map mapTypes = JSON.parseObject(getresult);
        if(!mapTypes.get("success").toString().equals("true")){
            throw new Exception("下单失败");
        }
        Map dataMap = JSON.parseObject(mapTypes.get("result")+"");
        String outTradeNo=String.valueOf(dataMap.get("outTradeNo"));
        String tradeprodNo=String.valueOf(dataMap.get("tradeprodNo"));
        String tradeNo=String.valueOf(dataMap.get("tradeNo"));


        Map<String,String> translateResultData=new HashMap<String,String>();
        translateResultData.put("SERVICE","mobile.security.pay");
        translateResultData.put("MERCHANTID", Params.merchantNo);
        translateResultData.put("SUBMERCHANTID","deprecated");
        translateResultData.put("ORDERSEQ",tradeprodNo);
        translateResultData.put("ORDERREQTRANSEQ",outTradeNo);
        translateResultData.put("ORDERTIME",Params.ORDERTIME);
        translateResultData.put("ORDERVALIDITYTIME","");
        translateResultData.put("ORDERAMOUNT",tradeAmt);  //元
        translateResultData.put("CURTYPE",Params.ccy);
        translateResultData.put("PRODUCTDESC",Params.PRODUCTDESC);
        translateResultData.put("SWTICHACC",Params.SWTICHACC);
        translateResultData.put("SUBJECT",subject);
        translateResultData.put("INSTITUTIONCODE",Params.INSTITUTIONCODE);
        translateResultData.put("INSTITUTIONTYPE",Params.INSTITUTIONTYPE);
        translateResultData.put("TRADENO",tradeNo);

        //对translateResultData按照字典顺序排序，获得content1
        String content1 = HttpClientPost.assembelSignaturingData(translateResultData);
        //对content1进行加密获得sign
        String sign = SignatureUtil.sign(InspectionSign.getSignture(), content1, (PrivateKey) InspectionSign.getKey().getPrivateKey());
       //TODO 支付成功跳转页面
        translateResultData.put("BEFOREMERCHANTURL",Params.BEFOREMERCHANTURL);
        translateResultData.put("BACKMERCHANTURL",minioConfig.getPayCallBackUrl()+"/app/pay/callbackPay");
        translateResultData.put("SIGNTYPE","CA");
        translateResultData.put("SIGN",sign);
        translateResultData.put("CURTYPE","156");
        translateResultData.put("PRODUCTAMOUNT",tradeAmt);
        translateResultData.put("ATTACHAMOUNT","0");
        translateResultData.put("ACCOUNTID",Params.ACCOUNTID);
        translateResultData.put("CUSTOMERID",Params.ACCOUNTID);

        //拼接请求报文请求参数，获得content
        String content = HttpClientPost.assembelSignaturingData(translateResultData);
        logger.info("支付请求参数:"+content);

        // 进行RSA+AES256加密，拼凑url
        String key = AES256.getStringRandom(32);
        String encryStr = AES256.AES_Encode(content, key);
        key = RSA.encrypt(key, rsaKeyBO.getPubKey(), SysConstants.SYS_CHARSET);
        String webUrl = "https://capi.bestpay.com.cn" + "/gateway.pay" +
                "?platform=wap_3.0" +
                "&encryStr=" + encryStr +
                "&keyIndex=" + rsaKeyBO.getKeyIndex() +
                "&encryKey=" + key;
        return webUrl.replaceAll("\\+", "%2B");
    }

    /**
     * @param outRequestNo 退款单号
     * @param outTradeNo 需要退款的订单号
     * @return
     * @throws InvalidKeySpecException
     * @throws NoSuchAlgorithmException
     * @throws IOException
     * @throws InvalidKeyException
     * @throws SignatureException
     */
    public Object refund(String outRequestNo,String outTradeNo,String refundAmt,String originalTradeDate) throws InvalidKeySpecException, NoSuchAlgorithmException, IOException, InvalidKeyException, SignatureException {
        //请求参数
        Map<String,String> translateResultData=new HashMap<String, String>();
        //退款
        translateResultData.put("outRequestNo",outRequestNo);//每次修改 退款流水号 商户侧生成
        translateResultData.put("merchantNo",Params.merchantNo); //商户号
        translateResultData.put("outTradeNo",outTradeNo);//原outTradeNo   交易订单号
        translateResultData.put("refundAmt",YuanRpmUtil.getTradeAmt(refundAmt)); // 单位 分  退款金额
        translateResultData.put("requestDate",Params.requestDate);
        translateResultData.put("operator",Params.merchantNo);
        translateResultData.put("tradeChannel",Params.tradeChannel);
        translateResultData.put("accessCode","CASHIER");
        translateResultData.put("ccy",Params.ccy);
        translateResultData.put("institutionCode",Params.INSTITUTIONCODE);
        //TODO回调url
        translateResultData.put("notifyUrl",minioConfig.getPayCallBackUrl()+"/app/pay/callBackRefund");
        translateResultData.put("institutionType",Params.INSTITUTIONTYPE);
        translateResultData.put("originalTradeDate",originalTradeDate);//原交易日期  取下单时间

        String content = HttpClientPost.assembelSignaturingData(translateResultData);
        String sign = SignatureUtil.sign(InspectionSign.getSignture(),content, (PrivateKey) InspectionSign.getKey().getPrivateKey());
        translateResultData.put("sign",sign);
        String s = JSON.toJSONString(translateResultData);
        String url = "https://mapi.bestpay.com.cn/mapi/uniformReceipt/tradeRefund";  // 退款接口
        String resposeData = HttpClientPost.reqPost(url,s);
        //如果退款成功，则生成退款结果
        boolean result=false;
        JSONObject json = JSONObject.parseObject(resposeData);
        if(json.getBoolean("success")){
            result =true;
        }else{
            json.get("errorMsg");
            logger.error(json.get("errorMsg"));
            result = false;
        }
        return  result;

    }

    /**
     * 支付回调接口处理
     * @param request
     * @return
     * @throws IOException
     */
    public Object callbackPay(HttpServletRequest request) throws IOException, InvalidKeySpecException, NoSuchAlgorithmException, InvalidKeyException, SignatureException {

        BufferedReader br = new BufferedReader(new InputStreamReader(request.getInputStream()));
        String line = null;
        StringBuilder sb = new StringBuilder();
        while((line = br.readLine())!=null){
            sb.append(line);
        }

        // 将资料解码
        String reqBody = sb.toString();
        String result = URLDecoder.decode(reqBody, HTTP.UTF_8);

        JSONObject jsonObject = JSONObject.parseObject(result);
        Map<String,Object> map = new HashMap<String,Object>();

        if (jsonObject.getString("tradeStatus").equals("SUCCESS")){
            //验签
            String content = HttpClientPost.assembelSignaturingData(jsonObject);
            String sign = SignatureUtil.sign(InspectionSign.getSignture(),content, (PrivateKey) InspectionSign.getKey().getPrivateKey());
            if(!sign.equals(jsonObject.getString("sign"))){
                return MessageResult.error("验签失败");
            }

            if(!Params.merchantNo.equals(jsonObject.getString("merchantNo"))){
                return MessageResult.error("验证商户失败");
            }
            map.put("success",true);

            Map<String,Object> data = new HashMap<String,Object>();
            data.put("statusCode",200);
            data.put("outTradeNo",jsonObject.get("outTradeNo"));
            data.put("tradeNo",jsonObject.get("tradeNo"));
            map.put("result",data);
            return map;
        }else{
            return MessageResult.error();
        }

    }

    /**
     * 退款回调
     * @param request
     * @return
     * @throws IOException
     */
    public Object callBackRefund(HttpServletRequest request) throws IOException, InvalidKeySpecException, NoSuchAlgorithmException, InvalidKeyException, SignatureException {
        BufferedReader br = new BufferedReader(new InputStreamReader(request.getInputStream()));
        String line = "";
        StringBuilder sb = new StringBuilder();
        while((line = br.readLine())!=null){
            sb.append(line);
        }

        // 将资料解码
        String reqBody = sb.toString();
        String result = URLDecoder.decode(reqBody, HTTP.UTF_8);

        JSONObject jsonObject = JSONObject.parseObject(result);
        Map<String,Object> map = new HashMap<String,Object>();
        if (jsonObject.getString("tradeStatus").equals("SUCCESS")){

            //验签
            String content = HttpClientPost.assembelSignaturingData(jsonObject);
            String sign = SignatureUtil.sign(InspectionSign.getSignture(),content, (PrivateKey) InspectionSign.getKey().getPrivateKey());
           if(!sign.equals(jsonObject.getString("sign"))){
               return MessageResult.error("验签失败");
           }

            if(!Params.merchantNo.equals(jsonObject.getString("merchantNo"))){
                return MessageResult.error("验证商户失败");
            }

            //验证订单号、金额

            //
           // 1、商户需要验证该通知数据中的outTradeNo是否为商户系统中创建的商户订单号
            //2、判断tradeAmt是否确实为该订单的实际金额（即商户订单创建时的金额）
            //3、校验通知中的tradeNo是否为outTradeNo这笔订单对应的翼支付交易号（下单接口会返回此值）
            //4、验证merchantNo是否为商户本身。

            //5.修改订单状态
            map.put("success",true);

            Map<String,Object> data = new HashMap<String,Object>();
            data.put("statusCode",200);
            data.put("outTradeNo",jsonObject.get("outTradeNo"));
            data.put("tradeNo",jsonObject.get("tradeNo"));
            map.put("result",data);
            return map;
        }else{
            return MessageResult.error();
        }
    }

}
